﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Markup;
using System.Xml;
using DiagramDesigner.Common.Adorners;
using DiagramDesigner.Common.Interfaces;
using DiagramDesigner.Common.Services.Interfaces;
using Microsoft.Practices.ServiceLocation;

namespace DiagramDesigner.Common.Controls
{
    [Export(typeof(ICanvasService))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class DesignerCanvas : Canvas, ICanvasService
    {
        private readonly List<IBehaviorPlugin> _behaviorPlugins;
        private Point? _rectangleSelectionStartPoint;

        public DesignerCanvas()
        {
            _behaviorPlugins = ServiceLocator.Current.GetInstance<IPluginsLoaderService<IBehaviorPlugin>>().
                GetPlugins().Where(p => p.ShowInToobox).ToList();
        }

        public IEnumerable<DesignerItem> SelectedItems
        {
            get { return Children.OfType<DesignerItem>().Where(i => i.IsSelected); }
        }

        public void DeselectAll(DesignerItem itemToExcept = null)
        {
            Type[] types = _behaviorPlugins.Select(p => p.BehaviorType).ToArray();
            foreach (DesignerItem item in SelectedItems)
            {
                if (itemToExcept != null && item.Equals(itemToExcept)) continue;
                item.IsSelected = false;
                DetachNonDefaultBehaviors(item, types);
            }
        }

        private void DetachNonDefaultBehaviors(DesignerItem item, IEnumerable<Type> types)
        {
            BehaviorCollection allBehaviors = Interaction.GetBehaviors(item);
            foreach (Type type in types)
            {
                Behavior currentBehavior = allBehaviors.SingleOrDefault(b => b.GetType() == type);
                if (currentBehavior != null)
                    allBehaviors.Remove(currentBehavior);
            }
        }

        public DesignerCanvas GetCanvas()
        {
            return this;
        }

        protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Source == this)
            {
                DeselectAll();
                Focus();
                _rectangleSelectionStartPoint = e.GetPosition(this);
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.LeftButton != MouseButtonState.Pressed) _rectangleSelectionStartPoint = null;
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (_rectangleSelectionStartPoint.HasValue)
                AdornerLayer.GetAdornerLayer(this).Add(new RectangularSelectionAdorner(this, _rectangleSelectionStartPoint.Value));
            }
        }

        protected override void OnDrop(DragEventArgs e)
        {
            List<IBehaviorPlugin> behaviorPlugins = ServiceLocator.Current.GetInstance<IPluginsLoaderService<IBehaviorPlugin>>().
                GetPlugins().Where(plugin => plugin.AutoAttachOnDrop).ToList();

            base.OnDrop(e);
            string xamlString = e.Data.GetData("DESIGNER_ITEM") as string;
            if (!String.IsNullOrEmpty(xamlString))
            {
                DesignerItem designerItem = null;
                FrameworkElement content = XamlReader.Load(XmlReader.Create(new StringReader(xamlString))) as FrameworkElement;

                if (content != null)
                {
                    designerItem = new DesignerItem();
                    designerItem.IsHitTestVisible = true;
                    designerItem.Content = content;

                    Point position = e.GetPosition(this);
                    if (content.MinHeight != 0 && content.MinWidth != 0)
                    {
                        designerItem.Width = content.MinWidth*2;
                        designerItem.Height = content.MinHeight*2;
                    }
                    else
                    {
                        designerItem.Width = 65;
                        designerItem.Height = 65;
                    }
                    SetLeft(designerItem, Math.Max(0, position.X - designerItem.Width/2));
                    SetTop(designerItem, Math.Max(0, position.Y - designerItem.Height/2));
                    
                    
                    foreach (IBehaviorPlugin behaviorPlugin in behaviorPlugins)
                    {
                        Interaction.GetBehaviors(designerItem).Add(behaviorPlugin.GetBehaviour());
                    }
                    

                    this.Children.Add(designerItem);

                    this.DeselectAll();
                    designerItem.IsSelected = true;
                    
                }

                e.Handled = true;
            }
        }

        protected override Size MeasureOverride(Size constraint)
        {
            Size size = new Size();

            foreach (UIElement element in this.InternalChildren)
            {
                double left = Canvas.GetLeft(element);
                double top = Canvas.GetTop(element);
                left = double.IsNaN(left) ? 0 : left;
                top = double.IsNaN(top) ? 0 : top;

                //measure desired size for each child
                element.Measure(constraint);

                Size desiredSize = element.DesiredSize;
                if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
                {
                    size.Width = Math.Max(size.Width, left + desiredSize.Width);
                    size.Height = Math.Max(size.Height, top + desiredSize.Height);
                }
            }
            // add margin 
            size.Width += 10;
            size.Height += 10;
            return size;
        }
    }
}
